package com.jrx.anytxn.investor.handler.impl;

import com.jrx.anytxn.common.constant.CommonContants;
import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.function.UncheckedUnaryOperator;
import com.jrx.anytxn.common.uitl.*;
import com.jrx.anytxn.customer.entity.CmCustomerSecondLevel;
import com.jrx.anytxn.customer.service.ICustomerService;
import com.jrx.anytxn.feign.gateway.bean.AcctInfo;
import com.jrx.anytxn.feign.gateway.bean.ApiResponse;
import com.jrx.anytxn.feign.gateway.bean.FreezeInfo;
import com.jrx.anytxn.feign.gateway.request.CoreRequest04;
import com.jrx.anytxn.feign.gateway.request.CoreRequest09;
import com.jrx.anytxn.feign.gateway.request.CoreRequest10;
import com.jrx.anytxn.feign.gateway.response.CoreResponse04;
import com.jrx.anytxn.feign.gateway.response.CoreResponse09;
import com.jrx.anytxn.feign.gateway.response.CoreResponse10;
import com.jrx.anytxn.feign.gateway.service.IInnerGatewayService;
import com.jrx.anytxn.feign.param.request.HandleInvestorLimitReq;
import com.jrx.anytxn.feign.param.service.IAnyTxnParamService;
import com.jrx.anytxn.investor.bean.*;
import com.jrx.anytxn.investor.constant.TransBizConstant;
import com.jrx.anytxn.investor.dto.loan.InvestorCancelLoanReq;
import com.jrx.anytxn.investor.dto.loan.InvestorCancelLoanRes;
import com.jrx.anytxn.investor.dto.loan.LoanReq;
import com.jrx.anytxn.investor.dto.loan.LoanRes;
import com.jrx.anytxn.investor.entity.*;
import com.jrx.anytxn.investor.handler.IInvestorLoanHandler;
import com.jrx.anytxn.investor.mapper.ext.*;
import com.jrx.anytxn.investor.service.*;
import com.jrx.anytxn.param.constant.Constant;
import com.jrx.anytxn.param.dto.aviator.RuleMatchReq;
import com.jrx.anytxn.param.dto.investor.FundTableRes;
import com.jrx.anytxn.param.dto.system.SystemParameterRes;
import com.jrx.anytxn.param.entity.PrFundFactor;
import com.jrx.anytxn.param.entity.PrInvestorInterestRate;
import com.jrx.anytxn.param.entity.PrOrganizationTable;
import com.jrx.anytxn.param.entity.PrProductTable;
import com.jrx.anytxn.param.service.aviator.IAviatorService;
import com.jrx.anytxn.param.service.investor.IFundTableService;
import com.jrx.anytxn.param.service.investor.IPrInvestorInterestRateService;
import com.jrx.anytxn.param.service.product.IProductTableService;
import com.jrx.anytxn.param.service.system.IOrganizationTableService;
import com.jrx.anytxn.param.service.system.ISystemParameterService;
import com.jrx.anytxn.transaction.entity.AmLoanMainInfo;
import com.jrx.anytxn.transaction.service.ILoanMainInfoService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 联合贷放款处理实现
 *
 * @author liuyang
 * @date 2020-05-18
 */
@Service
public class InvestorLoanHandlerImpl implements IInvestorLoanHandler {

    private static final Logger logger = LoggerFactory.getLogger(InvestorLoanHandlerImpl.class);

    @Autowired
    private IFundTableService fundTableService;

    @Autowired
    private IOrganizationTableService organizationTableService;

    @Autowired
    private IInvestorAccountService accountInvestorService;

    @Autowired
    private IInvestorRepaymentPlanService repaymentPlanInvestorService;

    @Autowired
    private IProductTableService productTableService;

    @Resource
    private ExtAmLoanMainInvestorInfoMapper extAmLoanMainInvestorInfoMapper;

    @Resource
    private ExtAmLoanAccountInvestorInfoMapper extAmLoanAccountInvestorInfoMapper;

    @Resource
    private ExtTlGlInterfaceInvestorInfoMapper extTlGlInterfaceInvestorInfoMapper;

    @Resource
    private ExtAmRepaymentPlanInvestorInfoMapper repaymentPlanInvestorInfoMapper;

    @Resource
    private ExtAmAccountMainInvestorInfoMapper extAmAccountMainInvestorInfoMapper;

    @Resource
    private ExtAmInterestAccrualInvestorInfoMapper extAmInterestAccrualInvestorInfoMapper;

    @Autowired
    private IInvestorGlInterfaceService investorGlInterfaceService;

    @Autowired
    private IPrInvestorInterestRateService interestService;

    @Autowired
    private IInvestorInterestCalculateService interestCalculateService;

    @Autowired
    private ExtTlInvestorInterestAccrualLogMapper investorInterestAccrualLogMapper;

    @Autowired
    private ILoanMainInfoService loanMainInfoService;

    @Autowired
    private ICustomerService customerService;

    @Lazy
    @Autowired
    private IInnerGatewayService innerGatewayService;

    @Resource
    private ExtTlFundRestraintLogMapper fundRestraintLogMapper;

    @Autowired
    private IInvestorLoanService investorLoanService;

    @Resource
    private ExtTlFundTransferLogMapper fundTransferLogMapper;

    @Resource
    private ExtTlFundTransferDetailMapper fundTransferDetailMapper;

    @Autowired
    private IAviatorService aviatorService;

    @Lazy
    @Autowired
    private IAnyTxnParamService anyTxnParamService;

    @Autowired
    private IInvestorLoanService investorLoanApproveService;

    @Autowired
    private ISystemParameterService systemParameterService;

    /**
     * 联合贷放款
     *
     * @param loanReq 放款请求信息
     * @return LoanRes 放款结果
     * @throws Exception
     * @author liuyang
     * @date 2020-05-18
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public LoanRes loan(LoanReq loanReq) throws Exception {

        LoanRes res = new LoanRes();
        res.setCustomerId(loanReq.getCustomerId());

        // 取得中间账户
        SystemParameterRes systemParameter = systemParameterService.findByParamCode(TransBizConstant.THIRD_ACCT_NO, loanReq.getTenantId());

        // 幂等验证 -- 因为该接口易处理超时，因此添加此判断
        List<AmLoanMainInvestorInfo> loanMainInvestorInfoList = extAmLoanMainInvestorInfoMapper.findInvestorLoan(loanReq.getLoanId(),loanReq.getCustomerId(),loanReq.getTenantId());
        if(CollectionUtils.isNotEmpty(loanMainInvestorInfoList)){
            res.setThirdAcctNo(systemParameter.getParamValue());
            return res;
        }

        // 获取订单信息
        AmLoanMainInfo loanMainInfo = loanMainInfoService.getAmLoanMainInfoByLoanIdAndCusId(loanReq.getLoanId(),loanReq.getCustomerId(),loanReq.getTenantId());

        // 获取业务日期
        PrOrganizationTable org = organizationTableService.findByOrId(loanMainInfo.getOrganizationId(),loanReq.getTenantId());
        Date businessDate = org.getNextProcessingDate();

        // 获取所有资金源出资方信息
        List<PrFundFactor> fundFactors = this.getFundFactors(loanReq.getTenantId(),loanMainInfo.getFundId());

        // 获取出资方id列表
        Set<String> investorIds = fundFactors.stream().map(PrFundFactor::getInvestorId).collect(Collectors.toSet());

        // 获取产品
        PrProductTable productTable = productTableService.findProByProIdAndOrgId(loanMainInfo.getProductId(), loanMainInfo.getOrganizationId(), loanReq.getTenantId());

        // 获取客户二级信息
        CmCustomerSecondLevel customerSecondLevel = customerService.getSecondByChannelAndCustomerIdAndTensnId(loanReq.getChannel(),loanReq.getCustomerId(),loanReq.getTenantId(), loanMainInfo.getProductId());

        // 所有非轧差资金方订单金额
        BigDecimal notOffsetLoanAmount = BigDecimal.ZERO;

        // 本行借款金额
        BigDecimal bankLoanAmount = BigDecimal.ZERO;

        List<AmLoanMainInvestorInfo> loanMainInvestorInfos = new ArrayList<>();
        List<AmLoanAccountInvestorInfo> loanAccountInvestorInfos = new ArrayList<>();
        List<AmRepaymentPlanInvestorInfo> repaymentPlanInvestorInfos = new ArrayList<>();
        List<AmAccountMainInvestorInfo> accountMainInvestorInfos = new ArrayList<>();
        List<AmInterestAccrualInvestorInfo> interestAccrualInvestorInfos = new ArrayList<>();
        List<TlGlInterfaceInvestorInfo> glInterfaceInvestorInfos = new ArrayList<>();
        List<TlInvestorInterestAccrualLog> investorInterestAccrualLogs = new ArrayList<>();
        List<TlFundRestraintLog> newfundRestraintLogs = new ArrayList<>();

        for (int i=0;i<fundFactors.size();i++) {

            PrFundFactor fundFactor = fundFactors.get(i);

            // 出资方订单金额计算
            BigDecimal loanAmount = null;
            if(i<fundFactors.size()-1){
                loanAmount = loanMainInfo.getLoanAmount().multiply(fundFactor.getInvertorPercentage()).setScale(CommonContants.BIGDECIMAL_SCALE, BigDecimal.ROUND_HALF_UP);
                // 汇总所有非轧差资金方订单金额
                notOffsetLoanAmount = notOffsetLoanAmount.add(loanAmount);
            } else {
                // 轧差资金方订单金额 = 对客资金方订单金额 - 所有非轧差资金方订单金额
                loanAmount = loanMainInfo.getLoanAmount().subtract(notOffsetLoanAmount);
            }

            // 本行的借款金额
            if (TransBizConstant.BANK_INVESTOR_ID.equals(fundFactor.getInvestorId())) {
                bankLoanAmount = loanAmount;
            }

            // 通过规则匹配获取出资方的利息，罚息，费用，提前还款手续费，提前结清手续费的id
            String productNumber = fundFactor.getFundId()+"-"+fundFactor.getInvestorId();
            InvestorTableIdBean investorTableIdBean = this.getInterestAndOtherTableId(loanReq, loanAmount, loanMainInfo.getOrganizationId(), customerSecondLevel.getCustomerGroup(), productNumber);

            // 构建贷款信息
            loanReq.setInvestorInterestTableId(investorTableIdBean.getInvestorInterestTableId());
            loanReq.setInvestorPenaltyTableId(investorTableIdBean.getInvestorPenaltyTableId());
            loanReq.setInvestorFeeTableId(investorTableIdBean.getInvestorFeeTableId());
            loanReq.setInvestorPrepayTableId(investorTableIdBean.getInvestorPrepayTableId());
            loanReq.setInvestorPayoffTableId(investorTableIdBean.getInvestorPayoffTableId());
            loanReq.setBusinessDate(businessDate);
            AmLoanMainInvestorInfo loanMainInvestorInfo = accountInvestorService.buildAmLoanMainInvestorInfo(loanMainInfo, loanReq, fundFactor, loanAmount);

            // 构建贷款账户
            AmLoanAccountInvestorInfo loanAccountInvestorInfo = accountInvestorService.buildAmLoanAccountInvestorInfo(loanMainInvestorInfo);

            // 生成还款计划
            InvestorLoanPlanParamBean planParamBean = repaymentPlanInvestorService.buildLoanPlanParamBean(businessDate, loanMainInfo.getPaymentDateDd(), productTable.getStatemntInterval(), repaymentPlanInvestorInfos);
            List<AmRepaymentPlanInvestorInfo> repaymentPlanList = repaymentPlanInvestorService.buildRepaymentPlanInvestor(planParamBean, loanMainInvestorInfo, loanAccountInvestorInfo);

            // 更新贷款信息和贷款账户
            accountInvestorService.updateLoanMainAndAccountInvestorInfo(loanMainInvestorInfo,loanAccountInvestorInfo,repaymentPlanList);

            // 抛当期本金、费用、利息累计
            InvestorCastAccountBean investorCastAccountBean = accountInvestorService.castTxnAccount(loanMainInvestorInfo,loanAccountInvestorInfo,repaymentPlanList,businessDate,false);

            // 如果开启当日借当日还，则利息累计账户记一天息
            if(TransBizConstant.LOAN_DAY_PAY_FLAG_1.equals(productTable.getLoanDayPayFlag())){
                List<InvestorIntrAccuBean> intrAccuBeanList = this.calInterest(loanMainInvestorInfo,loanAccountInvestorInfo,investorCastAccountBean.getInterestAccrualInfoList(),org);
                if(!CollectionUtils.isEmpty(intrAccuBeanList)){
                    intrAccuBeanList.stream()
                            .map(InvestorIntrAccuBean::getTlGlInterfaceInfoList)
                            .filter(CollectionUtils::isNotEmpty)
                            .forEach(glInterfaceInvestorInfos::addAll);

                    List<TlInvestorInterestAccrualLog> logList =
                            intrAccuBeanList.stream()
                                    .flatMap(log -> log.getTlInterestAccrualLogList().stream())
                                    .filter(Objects::nonNull)
                                    .collect(Collectors.toList());
                    investorInterestAccrualLogs.addAll(logList);
                }
            }

            // 构建贷款的总账流水
            TlGlInterfaceInvestorInfo glInterfaceInvestorInfo = investorGlInterfaceService.buildTlGlInterfaceInvestorInfo(loanMainInvestorInfo, loanAccountInvestorInfo, productTable.getChineseName(), businessDate, TransBizConstant.LIABILITY_FLAG_03);

            // 暂存数据
            loanMainInvestorInfos.add(loanMainInvestorInfo); // 贷款信息
            loanAccountInvestorInfos.add(loanAccountInvestorInfo); // 贷款账户
            repaymentPlanInvestorInfos.addAll(repaymentPlanList); // 还款计划
            accountMainInvestorInfos.addAll(investorCastAccountBean.getAccountMainInfoList()); // 费用交易账户
            interestAccrualInvestorInfos.addAll(investorCastAccountBean.getInterestAccrualInfoList()); // 累计交易账户
            glInterfaceInvestorInfos.addAll(investorCastAccountBean.getGlInterfaceInfoList()); // gl流水
            glInterfaceInvestorInfos.add(glInterfaceInvestorInfo); // 贷款的总账流水
        }

        // 查询资金限制流水
        List<TlFundRestraintLog> fundRestraintLogs = fundRestraintLogMapper.selectByLoanSeqNoInvestorId(loanReq.getSeqNo(), loanReq.getCustomerId(), investorIds, loanReq.getTenantId(), loanMainInfo.getFundId(), TransBizConstant.RESTRAINT_STATUS_1);

        // 构建资金转账流水
        TlFundTransferLog fundTransferLog = investorLoanService.buildTlFundTransferLog(loanMainInfo, loanReq.getSeqNo(), businessDate, TransBizConstant.TRANSFER_TYPE_1);

        // 写入资金转账明细
        List<TlFundTransferDetail> fundTransferDetails = investorLoanService.buildTlFundTransferDetails(fundRestraintLogs, fundTransferLog, TransBizConstant.CURRENCY_CNY, investorIds, bankLoanAmount);

        // 资金归集
        CoreRequest04 req04 = this.buildCoreRequest04(fundTransferLog, fundTransferDetails, loanReq.getTenantId(), fundTransferLog.getRefNo());
        ApiResponse<CoreResponse04> resp = innerGatewayService.transferAcct(JsonUtils.toJSon(req04));
        if (resp==null || TransBizConstant.GATEWAY_SUC!=resp.getCode()) {
            logger.error("订单[{}],资金归集异常：{}",loanReq.getLoanId(), JsonUtils.toJSon(resp));
            throw new TxnBizException("资金归集异常");
        }

        // 更新转账流水
        CoreResponse04 resp04 = resp.getData();
        fundTransferLog.setReturnCode(resp04.getBusCode());// 交易返回码
        fundTransferLog.setReturnMessage(resp04.getBusMsg());// 交易返回信息
        fundTransferLog.setReturnStatus(resp04.getDealState());// 交易状态
        fundTransferLog.setSumStatus(resp04.getDealResult());// 最终状态 
        fundTransferLog.setLaunchTime(new Date(resp.getStartTime()));// 交易发起时间
        fundTransferLog.setCompleteTime(new Date(resp.getEndTime()));// 支付完成时间

        // 资金归集失败
        if(!TransBizConstant.DEAL_RESULT_S.equals(resp.getData().getDealResult())){
            // 如果归集失败，则解冻支付资金
            for(TlFundRestraintLog fundRestraintLog : fundRestraintLogs){

                // 构建解冻的资金限制流水
                TlFundRestraintLog fundRestraintLog03 = investorLoanApproveService.buildTlFundRestraintLog(fundRestraintLog, TransBizConstant.FREEZE_OPTION_TYPE_03, fundRestraintLog.getRestraintNo());
                CoreRequest10 req10 = this.buildCoreRequest10(fundRestraintLog03, loanReq.getTenantId(), TransBizConstant.FREEZE_OPTION_TYPE_03);
                ApiResponse<CoreResponse10>  resp10 = innerGatewayService.freezeAccount(JsonUtils.toJSon(req10));
                String restraintStatus = null;
                if(resp10!=null && TransBizConstant.GATEWAY_SUC==resp10.getCode() && TransBizConstant.DEAL_STATE_S.equals(resp10.getData().getDealState())){
                    restraintStatus = TransBizConstant.RESTRAINT_STATUS_3;
                } else {
                    restraintStatus = TransBizConstant.RESTRAINT_STATUS_2;
                }
                fundRestraintLog03.setRestraintStatus(restraintStatus);
                newfundRestraintLogs.add(fundRestraintLog03);
            }
        }
        // 保存数据
        if (CollectionUtils.isNotEmpty(loanMainInvestorInfos)) {
            extAmLoanMainInvestorInfoMapper.batchInsertLoanMain(loanMainInvestorInfos); // 贷款信息
        }
        if (CollectionUtils.isNotEmpty(loanAccountInvestorInfos)) {
            extAmLoanAccountInvestorInfoMapper.batchInsertLoanAccount(loanAccountInvestorInfos); // 贷款账户
        }
        if (CollectionUtils.isNotEmpty(repaymentPlanInvestorInfos)) {
            repaymentPlanInvestorInfoMapper.batchInsertRepaymentPlanInfoList(repaymentPlanInvestorInfos); // 还款计划
        }
        if (CollectionUtils.isNotEmpty(accountMainInvestorInfos)) {
            extAmAccountMainInvestorInfoMapper.batchInsertAccount(accountMainInvestorInfos); // 费用交易账户
        }
        if (CollectionUtils.isNotEmpty(interestAccrualInvestorInfos)) {
            extAmInterestAccrualInvestorInfoMapper.batchInsertInterestAccrualInvestorInfo(interestAccrualInvestorInfos); // 累计交易账户
        }
        if (CollectionUtils.isNotEmpty(glInterfaceInvestorInfos)) {
            extTlGlInterfaceInvestorInfoMapper.batchInsertGl(glInterfaceInvestorInfos); // gl流水
        }
        if (CollectionUtils.isNotEmpty(investorInterestAccrualLogs)) {
            investorInterestAccrualLogMapper.batchInsertInvestorInterestAccrualLog(investorInterestAccrualLogs); // 出资方利息计提日志
        }
        if(fundTransferLog!=null){
            fundTransferLogMapper.insertSelective(fundTransferLog); // 资金转账流水
        }
        if(CollectionUtils.isNotEmpty(fundTransferDetails)){
            fundTransferDetailMapper.batchInsert(fundTransferDetails); // 资金转账明细
        }
        if(CollectionUtils.isNotEmpty(newfundRestraintLogs)){
            fundRestraintLogMapper.batchInsert(newfundRestraintLogs); // 资金限制流水
        }

        res.setThirdAcctNo(systemParameter.getParamValue());
        return res;
    }

    /**
     * 获取资金源出资方信息
     *
     * @param tenantId 租户id
     * @param fundId 资金源id
     * @return List<PrFundFactor> 出资方信息
     * @throws TxnException
     * @author liuyang
     * @date 2020-05-18
     */
    private List<PrFundFactor> getFundFactors(String tenantId, String fundId) throws TxnException {
        // 获取资金源出资方信息
        FundTableRes fundTableRes = fundTableService.findByFundIdAndTenId(tenantId,fundId);
        if (fundTableRes==null || CollectionUtils.isEmpty(fundTableRes.getControllerReqList())){
            logger.error(TxnRespCode.NO_DATA.getMsg());
            throw new TxnException(TxnRespCode.NO_DATA.getMsg());
        }
        List<PrFundFactor> fundFactors = fundTableRes.getControllerReqList();
        // 按pr_fund_factor的offset_flag从小到大排序
        fundFactors.sort(Comparator.comparing(PrFundFactor::getOffsetFlag));
        return fundFactors;
    }



    /**
     * 到账日起息，到账成功后处理
     *
     * @param loanId 贷款id
     * @param custId 客户id
     * @param tenantId 租户id
     * @param businessDate 业务日期
     * @return InvestorAfterSucSaveBean 到账日起息保存Bean
     * @throws TxnException
     * @author liuyang
     * @date 2020-06-03
     */
    @Override
    public InvestorAfterSucSaveBean afterLoanSuccuss(String loanId, String custId, String tenantId, Date businessDate) throws TxnException {

        // 初始化对象
        InvestorAfterSucSaveBean afterSucSaveBean = new InvestorAfterSucSaveBean();
        List<AmLoanMainInvestorInfo> loanMainInvestorInfos = new ArrayList<>();
        List<AmLoanAccountInvestorInfo> loanAccountInvestorInfos = new ArrayList<>();
        List<AmRepaymentPlanInvestorInfo> repaymentPlanInvestorInfos = new ArrayList<>();
        List<AmAccountMainInvestorInfo> accountMainInvestorInfos = new ArrayList<>();
        List<AmInterestAccrualInvestorInfo> interestAccrualInvestorInfos = new ArrayList<>();
        List<TlGlInterfaceInvestorInfo> glInterfaceInvestorInfos = new ArrayList<>();
        PrProductTable prProductTable = null;
        String orgId = null;

        // 获取所有出资方贷款信息
        List<AmLoanMainInvestorInfo> loanMainInvestorInfoList = extAmLoanMainInvestorInfoMapper.findInvestorLoan(loanId,custId,tenantId);

        for (AmLoanMainInvestorInfo loanMainInvestorInfo : loanMainInvestorInfoList) {

            // 获取机构id
            orgId = loanMainInvestorInfo.getOrganizationId();

            // 获取出资方利率参数信息
            PrInvestorInterestRate interest = interestService.findInterestByInterestId(loanMainInvestorInfo.getInvestorInterestTableId(), orgId, tenantId);
            // 如果交易生效日起息则返回null
            if (interest.getStartDateFlag().equals(TransBizConstant.NO_FLAG)){
                return null;
            }

            // 获取产品信息
            if(prProductTable == null) {
                prProductTable = productTableService.findProByProIdAndOrgId(loanMainInvestorInfo.getProductId(), orgId, tenantId);
            }

            // 获取出资方的订单账户信息
            AmLoanAccountInvestorInfo loanAccountInvestorInfo = extAmLoanAccountInvestorInfoMapper.selectByInvestorLoanIdAndCustId(loanMainInvestorInfo.getInvestorLoanId(), custId, tenantId);

            //生成还款计划
            InvestorLoanPlanParamBean planParamBean = repaymentPlanInvestorService.buildLoanPlanParamBean(businessDate,  loanMainInvestorInfo.getPaymentDateDd(), prProductTable.getStatemntInterval(), repaymentPlanInvestorInfos);
            List<AmRepaymentPlanInvestorInfo> repaymentPlanList = repaymentPlanInvestorService.buildRepaymentPlanInvestor(planParamBean, loanMainInvestorInfo, loanAccountInvestorInfo);

            // 更新贷款信息和贷款账户
            accountInvestorService.updateLoanMainAndAccountInvestorInfo(loanMainInvestorInfo,loanAccountInvestorInfo,repaymentPlanList);

            // 抛当期本金、费用、利息累计
            InvestorCastAccountBean investorCastAccountBean = accountInvestorService.castTxnAccount(loanMainInvestorInfo,loanAccountInvestorInfo,repaymentPlanList,businessDate,false);

            // 暂存数据
            loanMainInvestorInfos.add(loanMainInvestorInfo); // 贷款信息
            loanAccountInvestorInfos.add(loanAccountInvestorInfo); // 贷款账户
            repaymentPlanInvestorInfos.addAll(repaymentPlanList); // 还款计划
            accountMainInvestorInfos.addAll(investorCastAccountBean.getAccountMainInfoList()); // 交易账户
            interestAccrualInvestorInfos.addAll(investorCastAccountBean.getInterestAccrualInfoList()); // 累计交易账户
            glInterfaceInvestorInfos.addAll(investorCastAccountBean.getGlInterfaceInfoList()); // gl流水

            // 如果开启当日借当日还，则利息累计账户记一天息
            if(TransBizConstant.LOAN_DAY_PAY_FLAG_1.equals(prProductTable.getLoanDayPayFlag())){
                PrOrganizationTable org = organizationTableService.findByOrId(orgId,tenantId);
                List<InvestorIntrAccuBean> intrAccuBeanList = this.calInterest(loanMainInvestorInfo,loanAccountInvestorInfo,investorCastAccountBean.getInterestAccrualInfoList(),org);
                if(!CollectionUtils.isEmpty(intrAccuBeanList)){
                    intrAccuBeanList.stream()
                            .map(InvestorIntrAccuBean::getTlGlInterfaceInfoList)
                            .filter(CollectionUtils::isNotEmpty)
                            .forEach(glInterfaceInvestorInfos::addAll);

                    List<TlInvestorInterestAccrualLog> logList =
                            intrAccuBeanList.stream()
                                    .flatMap(log -> log.getTlInterestAccrualLogList().stream())
                                    .filter(Objects::nonNull)
                                    .collect(Collectors.toList());
                    afterSucSaveBean.setSaveIntrAccrualLogList(logList);
                }
            }
        }


        // 组装返回数据
        afterSucSaveBean.setUpdateLoanMainInvestorList(loanMainInvestorInfos); // 订单信息
        afterSucSaveBean.setUpdateLoanAccountInvestorList(loanAccountInvestorInfos); // 订单账户
        afterSucSaveBean.setSavePlanList(repaymentPlanInvestorInfos); // 还款计划
        afterSucSaveBean.setSaveAcctList(accountMainInvestorInfos); // 交易账户
        afterSucSaveBean.setSaveAccuAcctList(interestAccrualInvestorInfos); // 累计交易账户
        afterSucSaveBean.setSaveGlList(glInterfaceInvestorInfos); // gl流水

        return afterSucSaveBean;
    }

    /**
     * 计一天息
     *
     * @param accuAcctList 累计账户
     * @param org 机构
     * @throws TxnException
     * @author liuyang
     * @date 2020-06-03
     */
    private List<InvestorIntrAccuBean> calInterest(AmLoanMainInvestorInfo loanMainInfo,AmLoanAccountInvestorInfo loanAccountInfo,List<AmInterestAccrualInvestorInfo> accuAcctList, PrOrganizationTable org) throws TxnException {
        List<InvestorIntrAccuBean> intrAccuBeanList = new ArrayList<>();
        Date bussinessDate = org.getNextProcessingDate();
        Date accruDay = DateUtils.addDay(org.getAccruThruDate(), 1);
        for(AmInterestAccrualInvestorInfo accuAcct : accuAcctList) {
            InvestorDealIntrBean dealIntrBean = new InvestorDealIntrBean();
            dealIntrBean.setLoanAcct(loanAccountInfo);
            dealIntrBean.setLoanMain(loanMainInfo);
            dealIntrBean.setAccuAcct(accuAcct);
            dealIntrBean.setBusinessDate(bussinessDate);
            dealIntrBean.setAccruDay(accruDay);
            InvestorIntrAccuBean intrAccuBean = interestCalculateService.calculateInterest(dealIntrBean);
            if(null != intrAccuBean){
                intrAccuBeanList.add(intrAccuBean);
            }
        }
        return intrAccuBeanList;
    }



    /**
     * 联合贷放款失败取消订单
     *
     * @param req 联合贷放款失败取消订单请求实体
     * @return InvestorCancelLoanRes 联合贷放款失败取消订单响应实体
     * @throws Exception
     * @author liuyang
     * @date 2020-06-05
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public InvestorCancelLoanRes cancelLoan(InvestorCancelLoanReq req) throws Exception {

        // 初始化变量
        InvestorCancelLoanRes res = new InvestorCancelLoanRes();
        res.setCustomerId(req.getCustomerId());
        String loanId = req.getLoanId();
        String customerId = req.getCustomerId();
        String tenantId = req.getTenantId();
        String channel = req.getChannel();
        String fundId = req.getFundId();
        String productId = req.getProductId();

        List<AmLoanAccountInvestorInfo> updateLoanAccountInvestorInfos = null;
        List<AmRepaymentPlanInvestorInfo> updateRepaymentPlanInvestorInfos = null;
        List<AmAccountMainInvestorInfo> updateAccountMainInvestorInfos = null;
        List<AmInterestAccrualInvestorInfo> updateInterestAccrualInvestorInfos = null;
        List<TlGlInterfaceInvestorInfo> glInterfaceInvestorInfos = null;

        // 获取业务日期
        CmCustomerSecondLevel customerSecondLevel = customerService.getSecondByChannelAndCustomerIdAndTensnId(channel,customerId,tenantId, null);
        PrOrganizationTable organization = organizationTableService.findByOrId(customerSecondLevel.getOrganizationId(), tenantId);
        Date businessDate = organization.getNextProcessingDate();

        // 本行借款金额
        BigDecimal bankLoanAmount = BigDecimal.ZERO;

        // 取消订单数据,更新贷款状态account_status='9'(取消)
        List<AmLoanAccountInvestorInfo> loanAccountInvestorInfos = extAmLoanAccountInvestorInfoMapper.findInvestorLoan(loanId, customerId, tenantId);
        if(CollectionUtils.isNotEmpty(loanAccountInvestorInfos)){
            updateLoanAccountInvestorInfos = new ArrayList<>(loanAccountInvestorInfos.size());
            AmLoanAccountInvestorInfo updateObj = null;
            for (AmLoanAccountInvestorInfo obj : loanAccountInvestorInfos) {
                // 获取本行的借款金额
                if (TransBizConstant.BANK_INVESTOR_ID.equals(obj.getInvestorId())) {
                    AmLoanMainInvestorInfo loanMainInvestorInfo = extAmLoanMainInvestorInfoMapper.selectByInvestorLoanIdAndCustId(obj.getInvestorLoanId(), customerId, tenantId);
                    bankLoanAmount = loanMainInvestorInfo.getLoanAmount();
                }
                updateObj = new AmLoanAccountInvestorInfo();
                updateObj.setId(obj.getId());
                updateObj.setTenantId(obj.getTenantId());
                updateObj.setOrganizationId(obj.getOrganizationId());
                updateObj.setCustomerId(obj.getCustomerId());
                updateObj.setAccountStatus(TransBizConstant.LOAN_STATUS_9);
                updateLoanAccountInvestorInfos.add(updateObj);
            }
        }

        // 取消还款计划数据,更新还款计划状态plan_status='9'(取消)
        List<String> planStatusList = Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1);
        List<AmRepaymentPlanInvestorInfo> repaymentPlanInvestorInfos = repaymentPlanInvestorInfoMapper.selectByLoanIdAndStatus(loanId, planStatusList, customerId, tenantId);
        if(CollectionUtils.isNotEmpty(repaymentPlanInvestorInfos)){
            updateRepaymentPlanInvestorInfos = new ArrayList<>(repaymentPlanInvestorInfos.size());
            AmRepaymentPlanInvestorInfo updateObj = null;
            for (AmRepaymentPlanInvestorInfo obj : repaymentPlanInvestorInfos) {
                updateObj = new AmRepaymentPlanInvestorInfo();
                updateObj.setId(obj.getId());
                updateObj.setTenantId(obj.getTenantId());
                updateObj.setOrganizationId(obj.getOrganizationId());
                updateObj.setCustomerId(obj.getCustomerId());
                updateObj.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_9);
                updateRepaymentPlanInvestorInfos.add(updateObj);
            }
        }

        // 取消本金，费用交易账户,更新账户状态account_status='9'(取消)
        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_03, TransBizConstant.ACCOUNT_TYPE_06);
        List<String> statusList = Arrays.asList(TransBizConstant.ACCOUNT_STATUS_0,TransBizConstant.ACCOUNT_STATUS_1);
        List<AmAccountMainInvestorInfo> accountMainInvestorInfos = extAmAccountMainInvestorInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, statusList);
        if(CollectionUtils.isNotEmpty(accountMainInvestorInfos)){
            updateAccountMainInvestorInfos = new ArrayList<>(accountMainInvestorInfos.size());
            AmAccountMainInvestorInfo updateObj = null;
            for (AmAccountMainInvestorInfo obj : accountMainInvestorInfos) {
                updateObj = new AmAccountMainInvestorInfo();
                updateObj.setId(obj.getId());
                updateObj.setTenantId(obj.getTenantId());
                updateObj.setOrganizationId(obj.getOrganizationId());
                updateObj.setCustomerId(obj.getCustomerId());
                updateObj.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_9);
                updateAccountMainInvestorInfos.add(updateObj);
            }
        }

        // 取消利息累计交易账户,更新账户状态account_status='9'(取消)
        List<String> accuAcctTypeList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66);
        List<AmInterestAccrualInvestorInfo> interestAccrualInvestorInfos = extAmInterestAccrualInvestorInfoMapper.selectAllAccuAcct(loanId, customerId, tenantId, accuAcctTypeList, TransBizConstant.INTEREST_ACCRUAL_STATUS_1);
        if(CollectionUtils.isNotEmpty(interestAccrualInvestorInfos)){
            updateInterestAccrualInvestorInfos = new ArrayList<>(interestAccrualInvestorInfos.size());
            AmInterestAccrualInvestorInfo updateObj = null;
            for (AmInterestAccrualInvestorInfo obj : interestAccrualInvestorInfos) {
                updateObj = new AmInterestAccrualInvestorInfo();
                updateObj.setId(obj.getId());
                updateObj.setTenantId(obj.getTenantId());
                updateObj.setOrganizationId(obj.getOrganizationId());
                updateObj.setCustomerId(obj.getCustomerId());
                updateObj.setAccountStatus(TransBizConstant.INTEREST_ACCRUAL_STATUS_9);
                updateInterestAccrualInvestorInfos.add(updateObj);
            }
        }

        // 生成冲销gl流水
        List<TlGlInterfaceInvestorInfo> tlGlInterfaceInfoList = extTlGlInterfaceInvestorInfoMapper.selectAllGl(loanId, customerId, tenantId);
        UncheckedUnaryOperator<TlGlInterfaceInvestorInfo> cancelGL = gl -> {
            TlGlInterfaceInvestorInfo saveGl = investorGlInterfaceService.buildTlGlInterfaceInvestorInfo(gl, businessDate);
            return saveGl;
        };
        if (!CollectionUtils.isEmpty(tlGlInterfaceInfoList)) {
            glInterfaceInvestorInfos = tlGlInterfaceInfoList.stream()
                    .map(CollectorsUtils.tranToUncheck(cancelGL))
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
        }

        // 获取出资方id列表
        Set<String> investorIds = loanAccountInvestorInfos.stream().map(AmLoanAccountInvestorInfo::getInvestorId).collect(Collectors.toSet());

        // 查询资金限制流水
        List<TlFundRestraintLog> fundRestraintLogs = fundRestraintLogMapper.selectByLoanSeqNoInvestorId(req.getSeqNo(), customerId, investorIds, tenantId, fundId, TransBizConstant.RESTRAINT_STATUS_1);

        // 资金转账流水查询
        TlFundTransferLog fundTransferLog1 = fundTransferLogMapper.selectByLoanIdTypeStatus(loanId,TransBizConstant.TRANSFER_TYPE_1,TransBizConstant.TRANSFER_SUM_STATUS_S,tenantId);
        TlFundTransferLog fundTransferLog2 = fundTransferLogMapper.selectByLoanIdTypeStatus(loanId,TransBizConstant.TRANSFER_TYPE_2,TransBizConstant.TRANSFER_SUM_STATUS_S,tenantId);
        TlFundTransferLog fundTransferLog = null;
        List<TlFundTransferDetail> fundTransferDetails = null;
        if(fundTransferLog1!=null && fundTransferLog2==null){

            // 写入资金转账流水
            fundTransferLog = investorLoanService.buildTlFundTransferLog(fundTransferLog1, businessDate, TransBizConstant.TRANSFER_TYPE_2);

            // 写入资金转账明细
            fundTransferDetails = investorLoanService.buildTlFundTransferDetails(fundRestraintLogs, fundTransferLog, TransBizConstant.CURRENCY_CNY, investorIds, bankLoanAmount);

            // 支付资金冲正
            CoreRequest09 req09 = new CoreRequest09();
            req09.setServiceId(TransBizConstant.GATEWAY_SERVICE_ID); // 请求系统Id
            req09.setServiceIp(InternetUtils.getLocalIp()); // 请求ip地址
            req09.setTenantId(tenantId); // 租户Id
            req09.setServiceSeq(fundTransferLog.getRefNo()); // 请求流水号
            req09.setOrgTransDate(DateUtils.format(fundTransferLog1.getBusinessDate(), DateUtils.DATE_PATTERN_ANOTHER));// 原交易日期
            req09.setOrgSeq(fundTransferLog1.getSeqNo());// 原交易流水号
            ApiResponse<CoreResponse09> resp = innerGatewayService.coreToRectify(JsonUtils.toJSon(req09));
            if (resp==null || TransBizConstant.GATEWAY_SUC!=resp.getCode()) {
                logger.error("原交易流水号[{}],支付资金冲正异常：{}",fundTransferLog1.getSeqNo(), JsonUtils.toJSon(resp));
                throw new TxnBizException("支付资金冲正异常");
            }

            // 更新转账流水
            CoreResponse09 resp09 = resp.getData();
            fundTransferLog.setReturnCode(resp09.getBusCode());// 交易返回码
            fundTransferLog.setReturnMessage(resp09.getBusMsg());// 交易返回信息
            fundTransferLog.setReturnStatus(resp09.getDealState());// 交易状态
            fundTransferLog.setSumStatus(resp09.getDealResult());// 最终状态 
            fundTransferLog.setLaunchTime(new Date(resp.getStartTime()));// 交易发起时间
            fundTransferLog.setCompleteTime(new Date(resp.getEndTime()));// 支付完成时间
        }

        // 恢复出资方额度
        this.recoverInvestorLimit(fundRestraintLogs, channel, productId);

        // 保存数据
        if(CollectionUtils.isNotEmpty(updateLoanAccountInvestorInfos)){
            extAmLoanAccountInvestorInfoMapper.batchUpdate(updateLoanAccountInvestorInfos); // 订单账户
        }
        if(CollectionUtils.isNotEmpty(updateRepaymentPlanInvestorInfos)){
            repaymentPlanInvestorInfoMapper.batchUpdate(updateRepaymentPlanInvestorInfos); // 还款计划
        }
        if(CollectionUtils.isNotEmpty(updateAccountMainInvestorInfos)){
            extAmAccountMainInvestorInfoMapper.batchUpdate(updateAccountMainInvestorInfos); // 交易账户
        }
        if(CollectionUtils.isNotEmpty(updateInterestAccrualInvestorInfos)){
            extAmInterestAccrualInvestorInfoMapper.batchUpdate(updateInterestAccrualInvestorInfos); // 累计账户
        }
        if(CollectionUtils.isNotEmpty(glInterfaceInvestorInfos)){
            extTlGlInterfaceInvestorInfoMapper.batchInsertGl(glInterfaceInvestorInfos); // 冲销GL流水
        }
        if(fundTransferLog!=null){
            fundTransferLogMapper.insertSelective(fundTransferLog);// 资金转账流水
        }
        if(CollectionUtils.isNotEmpty(fundTransferDetails)){
            fundTransferDetailMapper.batchInsert(fundTransferDetails); // 资金转账明细
        }

        return res;
    }

    /**
     * 构建行内转账请求实体
     *
     * @param fundTransferLog 资金转账流水
     * @param fundTransferDetails 资金转账明细
     * @param tenantId 租户id
     * @param serviceSeq 请求流水号
     * @return CoreRequest04 行内转账请求实体
     * @throws Exception
     * @author liuyang
     * @date 2020-06-16
     */
    private CoreRequest04 buildCoreRequest04(TlFundTransferLog fundTransferLog, List<TlFundTransferDetail> fundTransferDetails, String tenantId, String serviceSeq) throws Exception {
        CoreRequest04 req = new CoreRequest04();
        req.setServiceId(TransBizConstant.GATEWAY_SERVICE_ID); // 请求系统Id
        req.setServiceIp(InternetUtils.getLocalIp()); // 请求ip地址
        req.setTenantId(tenantId); // 租户Id
        req.setServiceSeq(serviceSeq); // 请求流水号
        req.setCcy(TransBizConstant.CURRENCY_CNY); // 币种

        List<AcctInfo> acctList = fundTransferDetails.stream().map(detail -> {
            return new AcctInfo(detail.getAcctNo(), detail.getCrDrInd(), String.valueOf(detail.getAmount()), detail.getNarrative(), detail.getRestraintSeqNo());
        }).collect(Collectors.toList());

        req.setAcctList(acctList);

        return req;
    }

    /**
     * 构建账户冻结/解冻请求实体
     *
     * @param fundRestraintLog 资金源限制流水
     * @param tenantId 租户id
     * @param optionType 操作类型
     * @return CoreRequest10 账户冻结/解冻请求实体
     * @throws Exception
     * @author liuyang
     * @date 2020-06-16
     */
    private CoreRequest10 buildCoreRequest10(TlFundRestraintLog fundRestraintLog, String tenantId, String optionType) throws Exception {
        CoreRequest10 req = new CoreRequest10();
        req.setServiceId(TransBizConstant.GATEWAY_SERVICE_ID); // 请求系统Id
        req.setServiceIp(InternetUtils.getLocalIp()); // 请求ip地址
        req.setTenantId(tenantId); // 租户Id
        req.setServiceSeq(fundRestraintLog.getSeqNo()); // 请求流水号
        req.setAcctNo(fundRestraintLog.getAcctNo());//账号
        req.setFreezeArray(Arrays.asList(new FreezeInfo(optionType, fundRestraintLog.getRestraintNo(), String.valueOf(fundRestraintLog.getAmount()), null, null, null))); // 冻结列表
        return  req;
    }

    /**
     * 获取利率 罚息等数据ID（规则引擎）
     *
     * @param loanReq 贷款请求
     * @param organizationId 机构id
     * @param amount 交易金额
     * @param customerGroup 客群
     * @param  productNumber 编号
     * @return InvestorTableIdBean 联合贷参数id bean
     * @throws TxnBizException
     * @author liuyang
     * @date 2020-06-17
     */
    private InvestorTableIdBean getInterestAndOtherTableId(LoanReq loanReq, BigDecimal amount, String organizationId, String customerGroup, String productNumber) throws TxnBizException {

        InvestorTableIdBean bean = new InvestorTableIdBean();

        //规则匹配相关参数
        Map<String, Object> map = new HashMap<>();
        map.put(TransBizConstant.LOAN_RULE_TRANS_CHANNEL, loanReq.getChannel());//渠道
        map.put(TransBizConstant.LOAN_RULE_CUS_IDENTIFY, customerGroup);//客群
        map.put(TransBizConstant.LOAN_RULE_TENOR, loanReq.getTenor());//期数
        map.put(TransBizConstant.LOAN_RULE_TRANS_AMOUNT, amount);//交易金额

        // 1.获取利率表ID
        logger.debug("获取利率表ID");
        RuleMatchReq ruleMatchReq = new RuleMatchReq(organizationId, productNumber, TransBizConstant.PRO_INVESTOR_INTEREST_TABLE_ID, map, loanReq.getTenantId());
        String investorInterestTableId = (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        bean.setInvestorInterestTableId(investorInterestTableId);

        //2.获取提前结清参数
        logger.debug("提前结清参数表ID");
        ruleMatchReq.setRuleType(TransBizConstant.PRO_INVESTOR_PAYOFF_TABLE_ID);
        String investorPayoffTableId = (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        bean.setInvestorPayoffTableId(investorPayoffTableId);

        //3.罚息参数表ID
        logger.debug("罚息参数表ID");
        ruleMatchReq.setRuleType(TransBizConstant.PRO_INVESTOR_PENALTY_TABLE_ID);
        String investorPenaltyTableId = (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        bean.setInvestorPenaltyTableId(investorPenaltyTableId);

        //4.费用参数表id
        logger.debug("费用参数表id");
        ruleMatchReq.setRuleType(TransBizConstant.PRO_INVESTOR_FEE_TABLE_ID);
        String investorFeeTableId = (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        bean.setInvestorFeeTableId(investorFeeTableId);

        //5.提前还款手续费参数表id
        logger.debug("提前还款手续费参数表id");
        ruleMatchReq.setRuleType(TransBizConstant.PRO_INVESTOR_PRE_PAY_TABLE_ID);
        String investorPrepayTableId = (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        bean.setInvestorPrepayTableId(investorPrepayTableId);

        return bean;
    }

    /**
     * 恢复出资方额度
     *
     * @param fundRestraintLogs
     * @param channel
     * @param productId
     */
    private void recoverInvestorLimit(List<TlFundRestraintLog> fundRestraintLogs, String channel, String productId){
        for(TlFundRestraintLog fundRestraintLog : fundRestraintLogs){
            HandleInvestorLimitReq req = new HandleInvestorLimitReq(fundRestraintLog.getTenantId(), fundRestraintLog.getLoanSeqNo(), channel, fundRestraintLog.getInvestorId(), productId, fundRestraintLog.getAmount(), Constant.INVESTOR_LIMIT_CREDITTYPE_L, Constant.INVESTOR_LIMIT_USETYPE_R);
            anyTxnParamService.handleInvestorLimit(req);
        }
    }
}
